home *** CD-ROM | disk | FTP | other *** search
- /*
- * This software is copyright 1992 by Robert Morris.
- * You may freely redistribute this software as shareware
- * if you do so in the same form as you got it. If you find
- * this software useful, please send $12 to:
- * Robert Morris
- * P.O. Box 1044
- * Harvard Square Station
- * Cambridge, MA 02238
- * ecognome@aol.com
- * If you incorporate any of this software in any kind of
- * commercial product, please send $2 per copy distributed
- * to the above address.
- */
-
- /*
- * manipulate TIFF files.
- */
- #include "tiffinfo.h"
- #include "string.h"
-
- #ifdef TIFF_PRINTFS
- #include <stdio.h>
- #endif
-
- void TIFFError(TIFFPtr, OSErr, StringPtr);
- long ttohs(TIFFPtr, short);
- long ttohl(TIFFPtr, long);
- OSErr tiff_read(TIFFPtr, unsigned long, void *, long);
- OSErr ScanTIFFDirectory(TIFFPtr);
- OSErr ReadTIFFEntry(TIFFPtr, long offset, struct TIFFEntry *);
- long ParseScalar(TIFFPtr, struct TIFFEntry *);
- long *ParseArray(TIFFPtr, struct TIFFEntry *);
- CTabHandle TranslateColorMap(long *colorMap, int ncolors);
- CTabHandle MakeGrayTable(int depth, int zeroIsWhite);
- Ptr ThreeToFour(Ptr in, long nPixels);
- long RowBytes(TIFFPtr);
- void CopyPMStrip(PixMapHandle pm, Rect sr, Rect dr, int dither);
- void CopyBMStrip(BitMap *, Rect sr, Rect dr, int inv);
- void UnDifference(Ptr p, long rowbytes, long nrows, long span);
-
- /*
- * Scan a TIFF file's fields to ferret out interesting information.
- * Returns a TIFFPtr whether it succeeded or failed; the caller must
- * call GetTIFFError() to distinguish.
- */
- TIFFPtr
- ScanTIFF(int ref)
- {
- struct TIFFHeader header;
- TIFFPtr ti;
-
- ti = (TIFFPtr) NewPtr(sizeof(*ti));
- if(ti == 0)
- return(0);
- memset(ti, 0, sizeof(*ti)); /* clear the record */
-
- ti->ref = ref;
-
- if(tiff_read(ti, 0L, &header, sizeof(header)) != 0)
- return(ti);
-
- ti->byteOrder = header.byteOrder;
- if((ti->byteOrder != BigEndian && ti->byteOrder != LittleEndian) ||
- ttohs(ti, header.versionNumber) != 42){
- TIFFError(ti, -1, "\pNot a TIFF file.");
- return(ti);
- }
-
- ti->offset = ttohl(ti, header.offset);
- ScanTIFFDirectory(ti);
-
- return(ti);
- }
-
- void
- DisposeTIFF(TIFFPtr ti)
- {
- if(ti){
- if(ti->bitsPerSample)
- DisposPtr(ti->bitsPerSample);
- ti->bitsPerSample = 0;
- if(ti->colorMap)
- DisposCTable(ti->colorMap);
- ti->colorMap = 0;
- if(ti->stripOffsets)
- DisposPtr(ti->stripOffsets);
- ti->stripOffsets = 0;
- if(ti->stripByteCounts)
- DisposPtr(ti->stripByteCounts);
- ti->stripByteCounts = 0;
- DisposPtr(ti);
- }
- }
-
- OSErr
- ScanTIFFDirectory(TIFFPtr ti)
- {
- unsigned short nentries, entryno;
- struct TIFFEntry entry;
- long nextoffset, n, i, offset, *colorMap = 0;
- OSErr err;
-
- offset = ti->offset;
- ti->err = 0;
-
- /* assign default values */
- ti->bitsPerSample = (long *) NewPtr(sizeof(long));
- if(ti->bitsPerSample == 0){
- TIFFError(ti, err = MemError(), "\pOut of memory");
- return(err);
- }
- ti->bitsPerSample[0] = 1;
- ti->colorMap = 0;
- ti->compression = NoCompression;
- ti->predictor = NoPredictor;
- ti->imageLength = -1;
- ti->imageWidth = -1;
- ti->photometricInterpretation = -1;
- ti->planarConfiguration = PCContiguous;
- ti->rowsPerStrip = 0x7fffffff;
- ti->samplesPerPixel = 1;
- ti->stripByteCounts = 0;
- ti->stripOffsets = 0;
- ti->t6Options = 0;
- ti->fillOrder = 1;
-
- if((err = tiff_read(ti, ti->offset, &nentries, sizeof(nentries))) != 0)
- return(err);
- offset += sizeof(nentries);
- nentries = ttohs(ti, nentries);
-
- #define ZAP(x) {if(x){DisposPtr(x);} x = 0;}
-
- for(entryno = 0; entryno < nentries; entryno++){
- if((err = ReadTIFFEntry(ti, offset, &entry)) != 0)
- return(err);
- offset += sizeof(entry);
-
- switch(entry.tag){
- case ImageWidth:
- ti->imageWidth = ParseScalar(ti, &entry);
- break;
- case ImageLength:
- ti->imageLength = ParseScalar(ti, &entry);
- break;
- case BitsPerSample:
- ZAP(ti->bitsPerSample);
- ti->bitsPerSample = ParseArray(ti, &entry);
- break;
- case ColorMap:
- if(ti->colorMap)
- DisposCTable(ti->colorMap);
- ti->colorMap = 0;
- colorMap = ParseArray(ti, &entry);
- if(colorMap){
- ti->colorMap = TranslateColorMap(colorMap, entry.length / 3);
- DisposPtr(colorMap);
- colorMap = 0;
- if(ti->colorMap == 0)
- TIFFError(ti, -1, "\pCould not make color map.");
- }
- break;
- case Compression:
- ti->compression = ParseScalar(ti, &entry);
- break;
- case Predictor:
- ti->predictor = ParseScalar(ti, &entry);
- break;
- case PhotometricInterpretation:
- ti->photometricInterpretation = ParseScalar(ti, &entry);
- break;
- case StripByteCounts:
- ZAP(ti->stripByteCounts);
- ti->stripByteCounts = ParseArray(ti, &entry);
- break;
- case StripOffsets:
- ZAP(ti->stripOffsets);
- ti->stripOffsets = ParseArray(ti, &entry);
- break;
- case SamplesPerPixel:
- ti->samplesPerPixel = ParseScalar(ti, &entry);
- break;
- case RowsPerStrip:
- ti->rowsPerStrip = ParseScalar(ti, &entry);
- break;
- case PlanarConfiguration:
- ti->planarConfiguration = ParseScalar(ti, &entry);
- break;
- case FillOrder:
- ti->fillOrder = ParseScalar(ti, &entry);
- break;
- case T6Options:
- ti->t6Options = ParseScalar(ti, &entry);
- break;
- #ifdef TIFF_PRINTFS
- default:
- printf("tag %d, type %d, length %ld, value %ld/0x%lx\n",
- entry.tag, entry.type, entry.length, entry.offset, entry.offset);
- #endif
- }
- }
-
- /* Some NeXT program generates bogus PlanarConfigurations of 2. */
- if(ti->samplesPerPixel == 1 && ti->planarConfiguration == 2)
- ti->planarConfiguration = 1;
-
- if(ti->rowsPerStrip > ti->imageLength)
- ti->rowsPerStrip = ti->imageLength;
- ti->stripsPerImage = (ti->imageLength + ti->rowsPerStrip - 1) / ti->rowsPerStrip;
-
- /*
- * We need a color map for gray-scale images deeper than one bit.
- */
- if(ti->colorMap == 0 &&
- ti->samplesPerPixel == 1 &&
- ti->bitsPerSample[0] <= 8 &&
- ti->bitsPerSample[0] > 1 &&
- (ti->photometricInterpretation == PIZeroIsWhite ||
- ti->photometricInterpretation == PIZeroIsBlack)){
- ti->colorMap = MakeGrayTable(ti->bitsPerSample[0],
- ti->photometricInterpretation == PIZeroIsWhite);
- if(ti->colorMap == 0)
- TIFFError(ti, -1, "\pCould not make gray-scale color map.");
- }
-
- tiff_read(ti, offset, &nextoffset, sizeof(nextoffset));
- ti->nextOffset = ttohl(ti, nextoffset);
-
- return(0);
- }
-
- /*
- * returns 0 if OK, or a negative error #.
- */
- OSErr
- ReadTIFFEntry(TIFFPtr ti, long offset, struct TIFFEntry *ep)
- {
- OSErr err;
-
- if((err = tiff_read(ti, offset, ep, sizeof(struct TIFFEntry))) != 0)
- return(err);
- ep->tag = ttohs(ti, ep->tag);
- ep->type = ttohs(ti, ep->type);
- ep->length = ttohl(ti, ep->length);
- return(0);
- }
-
- /*
- * tries to interpret an entry as a single 32-bit integer value.
- * can handle either TIFF_SHORT or TIFF_LONG.
- * assumes ReadTIFFEntry() has already adjusted byte order of tag, type, and length.
- * returns 0 if OK, or a negative error #.
- * On the Mac, values appear in the highest bits of offset.
- */
- long
- ParseScalar(TIFFPtr ti, struct TIFFEntry *ep)
- {
- if(ep->length != 1)
- return(-1);
- if(ep->type == TIFF_BYTE){
- return((ep->offset >> 24) & 0xff);
- } else if(ep->type == TIFF_SHORT){
- return(ttohs(ti, (ep->offset >> 16) & 0xffff));
- } else if(ep->type == TIFF_LONG){
- return(ttohl(ti, ep->offset));
- } else {
- TIFFError(ti, -1, "\pUnknown value type.");
- return(-1);
- }
- }
-
- /*
- * Returns an array of longs, no matter what the size of the array element.
- * Note that if the array fits in the offset field, it will be there, in the
- * high-order bits, with element zero highest.
- */
- long *
- ParseArray(TIFFPtr ti, struct TIFFEntry *ep)
- {
- long nbytes, i;
- unsigned char *cp = 0;
- unsigned short *sp = 0;
- long *lp = 0;
-
- if(ep->length < 0)
- goto bad;
-
- lp = (long *) NewPtr(ep->length * sizeof(long));
- if(lp == 0)
- goto bad;
-
- if(ep->type == TIFF_BYTE){
- nbytes = ep->length;
- if(nbytes <= 4){
- for(i = 0; i < ep->length; i++)
- lp[i] = (ep->offset >> (24 - (i * 8))) & 0xff;
- } else {
- cp = (unsigned char *) NewPtr(nbytes);
- if(cp == 0)
- goto bad;
- if(tiff_read(ti, ttohl(ti, ep->offset), (char *)cp, nbytes) != 0)
- goto bad;
- for(i = 0; i < ep->length; i++)
- lp[i] = cp[i];
- }
- } else if(ep->type == TIFF_SHORT){
- nbytes = ep->length * 2;
- if(nbytes <= 4){
- for(i = 0; i < ep->length; i++)
- lp[i] = ttohs(ti, ep->offset >> (16 - (i * 16)));
- } else {
- sp = (unsigned short *) NewPtr(nbytes);
- if(sp == 0)
- goto bad;
- if(tiff_read(ti, ttohl(ti, ep->offset), (char *)sp, nbytes) != 0)
- goto bad;
- for(i = 0; i < ep->length; i++)
- lp[i] = ttohs(ti, sp[i]);
- }
- } else if(ep->type == TIFF_LONG){
- nbytes = ep->length * 4;
- if(nbytes <= 4){
- lp[0] = ttohl(ti, ep->offset);
- } else {
- if(tiff_read(ti, ttohl(ti, ep->offset), (char *)lp, nbytes) != 0)
- goto bad;
- for(i = 0; i < ep->length; i++)
- lp[i] = ttohl(ti, lp[i]);
- }
- } else {
- goto bad;
- }
-
- if(cp)
- DisposPtr(cp);
- if(sp)
- DisposPtr(sp);
- return(lp);
-
- bad:
- TIFFError(ti, -1, "\pInvalid array field.");
- if(cp)
- DisposPtr(cp);
- if(sp)
- DisposPtr(sp);
- if(lp)
- DisposPtr(lp);
- return(0);
- }
-
- /* convert depth (<= 8) to # of colors */
- int ColorsOfDepth[] = { 1, 2, 4, 8, 16, 32, 64, 128, 256 };
-
- /*
- * return a color map with ngrays shades of gray in it.
- */
- CTabHandle
- MakeGrayTable(int depth, int zeroIsWhite)
- {
- int i, ncolors;
- CTabHandle cth;
- RGBColor rgb;
-
- if(HasColorQD() == 0)
- return(0);
-
- if(depth >= 1 && depth <= 8)
- ncolors = ColorsOfDepth[depth];
- else
- return(0);
-
- cth = (CTabHandle) NewHandle((ncolors * sizeof(ColorSpec)) + 10);
- if(cth == 0)
- return(0);
-
- (*cth)->ctSeed = GetCTSeed();
- (*cth)->ctFlags = 0;
- (*cth)->ctSize = ncolors - 1;
-
- if(zeroIsWhite){
- for(i = 0; i < ncolors; i++){
- rgb.red = rgb.green = rgb.blue = (65535 / (ncolors - 1)) * (ncolors - i - 1);
- (*cth)->ctTable[i].value = i; /* this must be filled in... */
- (*cth)->ctTable[i].rgb = rgb;
- }
- } else {
- /* this doesn't work so hot on the Mac */
- for(i = 0; i < ncolors; i++){
- rgb.red = rgb.green = rgb.blue = (65535 / (ncolors - 1)) * i;
- (*cth)->ctTable[i].value = i; /* this must be filled in... */
- (*cth)->ctTable[i].rgb = rgb;
- }
- }
-
- return(cth);
- }
-
- /*
- * Turn a TIFF color table, which has red values, then green values, then blue values,
- * into a QuickDraw ColorTable.
- */
- CTabHandle
- TranslateColorMap(long *colorMap, int ncolors)
- {
- int i;
- CTabHandle cth;
- RGBColor rgb;
-
- if(HasColorQD() == 0)
- return(0);
-
- if(colorMap == 0 || ncolors <= 0 || ncolors > 256)
- return(0);
-
- cth = (CTabHandle) NewHandle((ncolors * sizeof(ColorSpec)) + 10);
- if(cth == 0)
- return(0);
-
- (*cth)->ctSeed = GetCTSeed();
- (*cth)->ctFlags = 0;
- (*cth)->ctSize = ncolors - 1;
-
- for(i = 0; i < ncolors; i++){
- rgb.red = colorMap[i];
- rgb.green = colorMap[i + ncolors];
- rgb.blue = colorMap[i + ncolors + ncolors];
- (*cth)->ctTable[i].value = i; /* this must be filled in... */
- (*cth)->ctTable[i].rgb = rgb;
- }
-
- return(cth);
-
- }
-
- long
- ttohs(TIFFPtr ti, short s)
- {
- long s1;
-
- if(ti->byteOrder == BigEndian){
- /* Mac-order */
- return(s & 0xffff);
- } else {
- s1 = (s >> 8) & 0xff;
- s1 |= (s & 0xff) << 8;
- return(s1);
- }
- }
-
- long
- ttohl(TIFFPtr ti, long l)
- {
- long l1;
- int i;
-
- if(ti->byteOrder == BigEndian){
- /* Mac-order */
- return(l);
- } else {
- l1 = 0;
- for(i = 0; i < 4; i++){
- l1 <<= 8;
- l1 |= (l & 0xff);
- l >>= 8;
- }
- return(l1);
- }
- }
-
- OSErr
- tiff_read(TIFFPtr ti, unsigned long offset, void *buf, long n)
- {
- int err;
- long count;
-
- err = SetFPos(ti->ref, fsFromStart, offset);
- if(err < 0){
- TIFFError(ti, err, "\pSeek failed.");
- return(err);
- }
-
- count = n;
- err = FSRead(ti->ref, &count, buf);
- if(err < 0 && err != eofErr){
- TIFFError(ti, err, "\pRead failed.");
- return(err);
- }
- if(count != n){
- TIFFError(ti, -1, "\pRead returned too little data.");
- return(eofErr);
- }
-
- return(0);
- }
-
- PixMapHandle
- PalettePixMap(short depth, CTabHandle cth, long x, long y, long width, long height,
- Ptr data, long row)
- {
- PixMapHandle pm;
-
- if(HasColorQD() == 0)
- return(0);
-
- /* Enforce restrictions. */
- if((row & 1) != 0 ||
- (((long) data) & 1) != 0 ||
- (depth != 1 && depth != 2 && depth != 4 && depth != 8) ||
- cth == 0){
- return(0);
- }
-
- pm = NewPixMap();
- if(pm == 0)
- return(0);
-
- (*pm)->pmVersion = 0; /* this is crucial */
- (*pm)->pmReserved = 0;
- (*pm)->packType = 0;
- (*pm)->packSize = 0;
- (*pm)->pixelType = 0; /* chunky */
- (*pm)->pixelSize = depth; /* bits per pixel */
- (*pm)->cmpCount = 1; /* 1 component per pixel */
- (*pm)->cmpSize = depth; /* bits per component */
- (*pm)->planeBytes = 0;
-
- (*pm)->bounds.left = x;
- (*pm)->bounds.right = x + width;
- (*pm)->bounds.top = y;
- (*pm)->bounds.bottom = y + height;
-
- (*pm)->baseAddr = data;
- (*pm)->rowBytes = row | 0x8000; /* the 0x8000 marks this as a Pixmap, not Bitmap */
-
- if((*pm)->pmTable == 0){
- DisposPixMap(pm);
- return(0);
- }
-
- /*
- * Give the PixMap its own color table, which DisposPixMap() frees.
- */
- MoveHHi(cth);
- HLock(cth);
- PtrToXHand(*cth, (*pm)->pmTable, GetHandleSize(cth));
- HUnlock(cth);
-
- return(pm);
- }
-
- PixMapHandle
- RGBPixMap(short depth, long x, long y, long width, long height,
- Ptr data, long rowbytes)
- {
- PixMapHandle pm;
-
- if(HasQD32() == 0)
- return(0);
-
- /* Enforce restrictions. */
- if((rowbytes & 1) != 0 ||
- (((long) data) & 1) != 0 ||
- (depth != 16 && depth != 32)){
- return(0);
- }
-
- pm = NewPixMap();
- if(pm == 0)
- return(0);
-
- (*pm)->pmVersion = 0; /* this is crucial */
- (*pm)->pmReserved = 0;
- (*pm)->packType = 0;
- (*pm)->packSize = 0;
- (*pm)->pixelType = 16; /* RGBDirect */
- (*pm)->pixelSize = depth; /* bits per pixel */
- (*pm)->cmpCount = 3; /* 1 component per pixel */
- (*pm)->cmpSize = (depth == 32 ? 8 : 5); /* bits per component */
- (*pm)->planeBytes = 0;
-
- (*pm)->bounds.left = x;
- (*pm)->bounds.right = x + width;
- (*pm)->bounds.top = y;
- (*pm)->bounds.bottom = y + height;
-
- (*pm)->baseAddr = data;
- (*pm)->rowBytes = rowbytes | 0x8000; /* the 0x8000 marks this as a Pixmap, not Bitmap */
-
- return(pm);
- }
-
- /*
- * Return (a*b)/c. Try to minimize the error.
- */
- #define MulDiv(a, b, c) (((a) * (b)) / (c))
-
- /*
- * Convert a from coordinate world aref to world bref. Result in b.
- * Optimized for aref and bref the same size.
- */
- void
- ScaleRect(Rect *aref, Rect *a, Rect *bref, Rect *b)
- {
- long hmul, hdiv, hoff;
- long vmul, vdiv, voff;
-
- hmul = bref->right - bref->left;
- hdiv = aref->right - aref->left;
-
- hoff = bref->left - MulDiv(aref->left, hmul, hdiv);
-
- vmul = bref->bottom - bref->top;
- vdiv = aref->bottom - aref->top;
-
- voff = bref->top - MulDiv(aref->top, vmul, vdiv);
-
- b->left = MulDiv(a->left, hmul, hdiv) + hoff;
- b->right = MulDiv(a->right, hmul, hdiv) + hoff;
- b->top = MulDiv(a->top, vmul, vdiv) + voff;
- b->bottom = MulDiv(a->bottom, vmul, vdiv) + voff;
- }
-
- /*
- * Calculate how many (uncompressed) bytes in a scanline.
- * Doesn't work for separated planes.
- * Returns -1 on error.
- */
- long
- RowBytes(TIFFPtr ti)
- {
- long rowbytes;
-
- if(ti->planarConfiguration != PCContiguous){
- TIFFError(ti, -1, "\pCannot understand planar data.");
- return(-1);
- }
-
- rowbytes = (ti->bitsPerSample[0] * ti->samplesPerPixel * ti->imageWidth + 7) / 8;
-
- return(rowbytes);
- }
-
- /*
- * Read a reasonable number of scan lines from an image, uncompress them if
- * necessary, and return a new pointer to the data. For compressed images,
- * this routine always reads exactly one strip. Since some applications write
- * large uncompressed images as a single strip, this routine tries to read
- * only about 64K at a time. ReadStrip() returns the number of lines actually
- * read in *linesread, and the first line actually read in *firstread. The
- * latter will always be <= startline.
- */
- Ptr
- ReadStrip(TIFFPtr ti, long startline, long *firstread, long *linesread)
- {
- long count, rowbytes, offset, uncount, i, nrows, strip;
- long skip, suboffset;
- Ptr p = 0, p1 = 0;
- Ptr srcPtr, dstPtr;
-
- /*
- * Decide which strip to read.
- */
- for(strip = 0; strip < ti->stripsPerImage; strip++)
- if(startline >= strip*ti->rowsPerStrip && startline < (strip+1)*ti->rowsPerStrip)
- break;
- if(strip >= ti->stripsPerImage){
- TIFFError(ti, -1, "\pBad arg to ReadStrip()");
- return(0);
- }
-
- /*
- * Decide how many rows in this strip; the last strip might have
- * fewer than the others.
- */
- nrows = ti->rowsPerStrip;
- if((strip * ti->rowsPerStrip) + nrows > ti->imageLength)
- nrows = ti->imageLength - (strip * ti->rowsPerStrip);
-
- if(ti->stripOffsets == 0){
- TIFFError(ti, -1, "\pNo strip offsets.");
- return(0);
- }
- offset = ti->stripOffsets[strip];
-
- if(ti->compression == NoCompression){
- /*
- * Just read part of a strip.
- */
- if((rowbytes = RowBytes(ti)) == -1)
- return(0);
- skip = startline - (strip * ti->rowsPerStrip);
- suboffset = offset + (skip * rowbytes);
- nrows -= skip;
- #define BUFSIZE 64000L
- if(rowbytes >= BUFSIZE){
- nrows = 1;
- } else if((nrows * rowbytes) > BUFSIZE){
- nrows = BUFSIZE / rowbytes;
- }
- count = rowbytes * nrows;
-
- p = NewPtr(count);
- if(p == 0){
- TIFFError(ti, MemError(), "\pOut of memory.");
- return(0);
- }
-
- if(tiff_read(ti, suboffset, p, count) != 0)
- goto bad;
-
- *firstread = startline;
- *linesread = nrows;
-
- return(p);
- }
-
- if(ti->stripByteCounts){
- count = ti->stripByteCounts[strip];
- } else {
- /* cannot guess strip length */
- return(0);
- }
-
- p = NewPtr(count);
- if(p == 0){
- TIFFError(ti, MemError(), "\pOut of memory.");
- return(0);
- }
-
- if(tiff_read(ti, offset, p, count) != 0)
- goto bad;
-
- if(ti->compression == T6Compression){
- rowbytes = RowBytes(ti);
- if(rowbytes == -1)
- goto bad;
- uncount = rowbytes * ti->rowsPerStrip;
- p1 = NewPtr(uncount);
- if(p1 == 0){
- TIFFError(ti, MemError(), "\pOut of memory.");
- goto bad;
- }
- if(DecodeT6(ti, p, count, p1, uncount) != 0)
- goto bad;
- DisposPtr(p);
- p = p1;
- p1 = 0;
- } else if(ti->compression == LZWCompression){
- rowbytes = RowBytes(ti);
- if(rowbytes == -1)
- goto bad;
- uncount = rowbytes * ti->rowsPerStrip;
- p1 = NewPtr(uncount);
- if(p1 == 0){
- TIFFError(ti, MemError(), "\pOut of memory.");
- goto bad;
- }
-
- if(UnLZW(p, count, p1, uncount) != 0){
- TIFFError(ti, -1, "\pLZW decompression failed.");
- goto bad;
- }
- DisposPtr(p);
- p = p1;
- p1 = 0;
-
- if(ti->predictor == HDPredictor &&
- ti->bitsPerSample[0] == 8 &&
- ti->planarConfiguration == PCContiguous){
- /* horizontal differencing */
- for(i = 0; i < ti->samplesPerPixel; i++)
- UnDifference(p+i, rowbytes, ti->rowsPerStrip, ti->samplesPerPixel);
- } else if(ti->predictor != NoPredictor){
- TIFFError(ti, -1, "\pUnimplemented LZW prediction type.");
- goto bad;
- }
- } else if(ti->compression == PackCompression){
- rowbytes = RowBytes(ti);
- if(rowbytes == -1)
- goto bad;
- if(rowbytes > 32767){
- TIFFError(ti, -1, "\pRow too long for UnpackBits.");
- goto bad;
- }
- uncount = rowbytes * ti->rowsPerStrip;
- p1 = NewPtr(uncount);
- if(p1 == 0){
- TIFFError(ti, MemError(), "\pOut of memory.");
- goto bad;
- }
- srcPtr = p;
- for(i = 0; i < nrows; i++){
- dstPtr = p1 + (i * rowbytes);
- /* does UnpackBits restrict the size of rowbytes? */
- UnpackBits(&srcPtr, &dstPtr, (int)rowbytes);
- }
- DisposPtr(p);
- p = p1;
- p1 = 0;
- } else {
- TIFFError(ti, -1, "\pUnimplemented compression type.");
- goto bad;
- }
-
- *firstread = strip * ti->rowsPerStrip;
- *linesread = nrows;
-
- return(p);
-
- bad:
- if(p)
- DisposPtr(p);
- if(p1)
- DisposPtr(p1);
- return(0);
- }
-
- /*
- * Draw a tiff file image in the current QuickDraw port.
- * The idea is to read each strip, convert into a form
- * that's usable as a QuickDraw PixMap, and copy it to the
- * current port. As an optimization, don't bother reading
- * strips that won't be visible.
- */
- OSErr
- DrawTIFF(int ref, TIFFPtr ti, Rect sr, Rect dr, int dither)
- {
- long y, stripHeight, dstStripHeight, i, rowbytes;
- Ptr p = 0, p1;
- PixMapHandle pm = 0;
- Rect visr, portr;
- GrafPtr port;
- int ppb, inv;
- BitMap bm;
-
- GetPort(&port);
-
- /*
- * Make sure we know how many bits there are per sample, and that
- * the Red, Green, and Blue channels are the same size for RGB images.
- */
- if(ti->bitsPerSample == 0){
- TIFFError(ti, -1, "\pNo bits-per-sample given.");
- return(-1);
- }
- for(i = 1; i < ti->samplesPerPixel; i++){
- if(ti->bitsPerSample[i] != ti->bitsPerSample[i-1]){
- TIFFError(ti, -1, "\pBits-per-sample are different.");
- return(-1);
- }
- }
-
- ti->ref = ref;
-
- /*
- * The destination rectangle may not be totally visible. As an optimization,
- * translate the bounding box of the visible area into the source image
- * space, and only copy the relevant strips.
- */
- portr = (*(port->visRgn))->rgnBBox;
- ScaleRect(&dr, &portr, &sr, &visr);
- y = visr.top;
- stripHeight = 0;
-
- for( ; y < visr.bottom && y < ti->imageLength; y += stripHeight){
- p = ReadStrip(ti, y, &y, &stripHeight);
- if(p == 0)
- goto bad;
-
- /*
- * Find a way to draw the strip.
- */
- if(ti->samplesPerPixel == 1 &&
- ti->bitsPerSample[0] == 1 &&
- (ti->photometricInterpretation == PIZeroIsBlack ||
- ti->photometricInterpretation == PIZeroIsWhite)){
- rowbytes = (ti->bitsPerSample[0] * ti->imageWidth + 7) / 8;
- inv = ti->photometricInterpretation == PIZeroIsBlack;
- if(rowbytes & 1){
- bm.bounds.right = ti->imageWidth;
- bm.rowBytes = rowbytes + 1;
- for(i = 0; i < stripHeight; i++){
- p1 = p + (i * rowbytes);
- bm.bounds.top = y + i;
- bm.bounds.bottom = y + i + 1;
- if((long)p1 & 1){
- bm.bounds.left = -8;
- bm.baseAddr = p1 - 1;
- } else {
- bm.bounds.left = 0;
- bm.baseAddr = p1;
- }
- CopyBMStrip(&bm, sr, dr, inv);
- }
- } else {
- bm.baseAddr = p;
- bm.rowBytes = rowbytes;
- bm.bounds.left = 0;
- bm.bounds.right = ti->imageWidth;
- bm.bounds.top = y;
- bm.bounds.bottom = y + stripHeight;
- CopyBMStrip(&bm, sr, dr, inv);
- }
- } else if(ti->samplesPerPixel == 3 &&
- ti->bitsPerSample[0] == 8 &&
- ti->planarConfiguration == PCContiguous &&
- ti->photometricInterpretation == PIRGB){
- /* convert from 3 to 4 bytes per pixel */
- p1 = ThreeToFour(p, ti->imageWidth * stripHeight);
- if(p1 == 0){
- TIFFError(ti, -1, "\pOut of memory.");
- goto bad;
- }
- rowbytes = 4 * ti->imageWidth;
- pm = RGBPixMap(32, 0, y, ti->imageWidth, stripHeight, p1, rowbytes);
- if(pm == 0){
- DisposPtr(p1);
- TIFFError(ti, -1, "\pCould not allocate PixMap.");
- goto bad;
- }
- CopyPMStrip(pm, sr, dr, dither);
- DisposPixMap(pm);
- pm = 0;
- DisposPtr(p1);
- } else if(ti->samplesPerPixel == 1 &&
- (ti->bitsPerSample[0] == 1 || ti->bitsPerSample[0] == 2 ||
- ti->bitsPerSample[0] == 4 || ti->bitsPerSample[0] == 8) &&
- (ti->photometricInterpretation == PIPalette ||
- ti->photometricInterpretation == PIZeroIsBlack ||
- ti->photometricInterpretation == PIZeroIsWhite)){
- rowbytes = (ti->bitsPerSample[0] * ti->imageWidth + 7) / 8;
- if(rowbytes & 1){
- /* must copy odd widths a line at a time */
- ppb = 8 / ti->bitsPerSample[0];
- pm = PalettePixMap(ti->bitsPerSample[0], ti->colorMap,
- 0, 0,
- ti->imageWidth, 1,
- 0, rowbytes + 1);
- if(pm == 0){
- TIFFError(ti, -1, "\pCould not allocate PixMap.");
- goto bad;
- }
- for(i = 0; i < stripHeight; i++){
- p1 = p + (i * rowbytes);
- (*pm)->bounds.top = y + i;
- (*pm)->bounds.bottom = y + i + 1;
- if((long)p1 & 1){
- (*pm)->bounds.left = -ppb;
- (*pm)->baseAddr = p1 - 1;
- } else {
- (*pm)->bounds.left = 0;
- (*pm)->baseAddr = p1;
- }
- CopyPMStrip(pm, sr, dr, dither);
- }
- DisposPixMap(pm);
- pm = 0;
- } else {
- pm = PalettePixMap(ti->bitsPerSample[0], ti->colorMap,
- 0, y,
- ti->imageWidth, stripHeight,
- p, rowbytes);
- if(pm == 0){
- TIFFError(ti, -1, "\pCould not allocate PixMap.");
- goto bad;
- }
- CopyPMStrip(pm, sr, dr, dither);
- DisposPixMap(pm);
- pm = 0;
- }
- } else {
- TIFFError(ti, -1, "\pUnimplemented image representation.");
- goto bad;
- }
-
- DisposPtr(p);
- p = 0;
- }
-
- return(0);
-
- bad:
- if(p)
- DisposPtr(p);
- if(pm)
- DisposPixMap(pm);
- return(-1);
- }
-
- /*
- * Copy a pixmap of a strip to the right place.
- */
- void
- CopyPMStrip(PixMapHandle pm, Rect sr, Rect dr, int dither)
- {
- Rect sr1, dr1;
- GrafPtr port;
-
- sr1 = (*pm)->bounds;
- sr1.left = sr.left;
- sr1.right = sr.right;
- if(sr1.top < sr.top)
- sr1.top = sr.top;
- if(sr1.bottom > sr.bottom)
- sr1.bottom = sr.bottom;
-
- ScaleRect(&sr, &sr1, &dr, &dr1);
-
- GetPort(&port);
-
- MoveHHi(pm);
- HLock(pm);
- CopyBits(*pm, &(port->portBits), &sr1, &dr1, dither ? 64 : srcCopy, 0L);
- HUnlock(pm);
- }
-
- /*
- * Copy a bitmap of a strip to the right place.
- * Optionally invert black and white.
- */
- void
- CopyBMStrip(BitMap *bm, Rect sr, Rect dr, int inv)
- {
- Rect sr1, dr1;
- GrafPtr port;
-
- sr1 = bm->bounds;
- sr1.left = sr.left;
- sr1.right = sr.right;
- if(sr1.top < sr.top)
- sr1.top = sr.top;
- if(sr1.bottom > sr.bottom)
- sr1.bottom = sr.bottom;
-
- ScaleRect(&sr, &sr1, &dr, &dr1);
-
- GetPort(&port);
-
- CopyBits(bm, &(port->portBits), &sr1, &dr1, inv ? notSrcCopy : srcCopy, 0L);
- }
-
-
- /*
- * Convert 3-bytes-per-pixel RGB data to 4-bytes-per-pixel, as required
- * for QuickDraw 32-bit Pix Maps. inLen is the number of bytes.
- */
- Ptr
- ThreeToFour(Ptr in, long nPixels)
- {
- Ptr out;
- register unsigned long *inp, *outp;
- register unsigned long in1, in2, in3;
- char *ip, *op;
-
- out = NewPtr(4 * nPixels);
- if(out == 0)
- return(0);
-
- inp = (unsigned long *) in;
- outp = (unsigned long *) out;
-
- for( ; nPixels >= 4; nPixels -= 4){
- /* read 4 pixels worth of data in 4 longs: RGBR GBRG BRGB */
- in1 = *inp++;
- in2 = *inp++;
- in3 = *inp++;
-
- /* write the 4 pixels as 4 longs */
- *outp++ = in1 >> 8;
- *outp++ = ((in1 & 0xff) << 16) | ((in2 >> 16) & 0xffff);
- *outp++ = ((in2 & 0xffff) << 8) | (in3 >> 24);
- *outp++ = in3 & 0xffffff;
- }
-
- /* take care of last 1, 2 or 3 pixels one byte at a time */
- ip = (char *) inp;
- op = (char *) outp;
- for( ; nPixels > 0; --nPixels){
- *op++ = 0;
- *op++ = *ip++;
- *op++ = *ip++;
- *op++ = *ip++;
- }
-
- return(out);
- }
-
- /*
- * Un-difference a strip of image. Must be 8-bit samples.
- * Span indicates how many bytes to skip (for interleaved RGB).
- * Modifies the data in place.
- */
- void
- UnDifference(Ptr p, long rowbytes, long nrows, long span)
- {
- long row, col, off;
-
- for(row = 0; row < nrows; row++){
- off = row * rowbytes;
- for(col = span; col < rowbytes; col += span){
- p[off + col] += p[off + col - span];
- }
- }
- }
-
- /*
- * Register an error with a TIFF record, for later retrieval with GetTIFFError().
- */
- void
- TIFFError(TIFFPtr ti, OSErr err, StringPtr p)
- {
- int i;
-
- if(ti && ti->err == 0){
- if(err == 0)
- err = -1;
- ti->err = err;
- for(i = 0; i < p[0] && i < 255; i++)
- ti->errStr[i+1] = p[i+1];
- ti->errStr[0] = p[0];
- }
- }
-
- /*
- * Return and clear the error associated with a TIFF record.
- */
- OSErr
- GetTIFFError(TIFFPtr ti, StringPtr p)
- {
- int i;
- OSErr err;
-
- p[0] = 0;
- if(ti == 0)
- return(-1);
-
- p[0] = ti->errStr[0];
- for(i = 0; i < p[0] && i < 255; i++)
- p[i+1] = ti->errStr[i+1];
-
- err = ti->err;
- ti->err = 0;
- ti->errStr[0] = 0;
-
- return(err);
- }
-
- /*
- * Can a TIFF image be drawn with this system's version of QuickDraw?
- */
- Boolean
- TIFFDrawable(TIFFPtr ti)
- {
- if(ti->samplesPerPixel > 1)
- return(HasQD32());
- if(ti->bitsPerSample[0] > 1)
- return(HasColorQD());
- return(TRUE);
- }